package exercici1;

import java.util.ArrayList;
import java.io.*;

/**
 * Clase que implementa las funciones de Restaurant.
 * 
 * @author Jose Antonio Prieto Carballo NIA 37682<br>
 * Iván Gómez González NIA 33387
 * 
 * @see Spain - Barcelona - UPF - Telemàtica - 3r - Aplicacions Telemàtiques 2 - Practica 9
 */
public class Restaurant {

    /** Lista de las mesas del restaurante */
    private static ArrayList<Mesa> mesas;
    /** Lista de meseros del restaurante */
    private static ArrayList<Mesero> meseros;
    /** Lista de productos disponibles en el restaurante */
    private static ArrayList<Producto> productos;

    /**
     * Constructor de la clase.
     * @param Meseros
     * @param numMesas
     * @param Productos
     */
    public Restaurant(String Meseros, int numMesas, String Productos) {
        // Inicialización de variables
        mesas = new ArrayList<Mesa>();
        meseros = new ArrayList<Mesero>();
        productos = new ArrayList<Producto>();
        
        // Comprobar que el fichero meseros y el fichero productos existen
        File meserosLista = new File(Meseros.toString());
        File productosLista = new File(Productos.toString());
        
        if (!meserosLista.exists() || !productosLista.exists()) {
            System.err.println("Ha habido un error al leer las listas de productos o de meseros.");
            System.exit(-1);
        }

        // si existen
        // leerlos y llenar los respectivos ArrayList
        BufferedReader listaMeseros = null;
        BufferedReader listaProductos = null;
        try {
            listaMeseros = new BufferedReader(new FileReader(meserosLista));
            listaProductos = new BufferedReader(new FileReader(productosLista));

            String elemento = "";

            // Llena el ArrayList de meseros
            while ((elemento = listaMeseros.readLine()) != null) {
                meseros.add(new Mesero(elemento));
            }

            // Llena el Arraylist de productos
            elemento = "";
            String[] sub_elementos = new String[3];
            while ((elemento = listaProductos.readLine()) != null) {
                sub_elementos = elemento.split("\t");
                
                ////////////////////////////////////////////////////////////////////////////////
                // Por convención TODOS los productos tendrán un identificador MAYOR que cero //
                ////////////////////////////////////////////////////////////////////////////////
                
                productos.add(new Producto(Integer.parseInt(sub_elementos[0]), sub_elementos[1], Integer.parseInt(sub_elementos[2])));
            }
            
            // Llena el ArrayList de mesas
            for (int i = 0; i != numMesas; i++) {
                
                //////////////////////////////////////////////////////////////////////////////////
                // Por convención TODAS las mesas tendrán un identificador MAYOR O IGUAL a cero //
                //////////////////////////////////////////////////////////////////////////////////
                
                mesas.add(new Mesa(i));
            }
            
            // Tratamiento frente a errores
            
        } catch (FileNotFoundException fnfe) {
            System.err.println("ERROR!! No se ha encontrado el archivo de meseros o de productos.");
            fnfe.printStackTrace();
            System.exit(-2);
        } catch (NumberFormatException nfe) {
            System.err.println("ERROR!! Ha habido un error de formato en alguna variable.");
            nfe.printStackTrace();
            System.exit(-3);
        } catch (IOException ioe) {
            System.err.println("ERROR!! Ha habido un error al leer los archivos de meseros o productos.");
            ioe.printStackTrace();
            System.exit(-4);
        } finally {
            try {
                listaMeseros.close();
                listaProductos.close();
            } catch (IOException ioe) {
                System.err.println("ERROR!! Ha habido un error al cerrar los buffers del archivo de meseros o productos.");
                ioe.printStackTrace();
            }
        }
    }

    /**
     * Función que retorna un String con la lista de productos disponibles en el restaurante.
     * @return String
     */
    public static String listarProductos() {
        // Cabecera
        String prod = "ID\t|\tProducto";
        // Añade a la cabecera todos los productos
        for (Producto p : productos) {
            prod += "\n" + p.getId() + "\t|\t" + p.getNombre();
        }
        return prod;
    }

    /**
     * Función que retorna un entero con los ingresos totales del restaurante hasta el momento.<br>
     * Obviamente no incluye los ingresos actuales, sino los ingresos acumulados de todas las mesas.
     * @return int
     */
    public static int ingresosTotales() {
        // Creamos e inicializamos la variable a retornar
        int ingresos = 0;
        // Para cada mesa
        for (Mesa me : mesas) { 
            // Acumulamos el valor de ingresos totales de todas las mesas
            ingresos += me.getIngresoAcumulado();
        }
        // Devolvemos el valor
        return ingresos;
    }

    /**
     * Función que retorna un String con un listado del número de mesas atendidas por cada mesero.
     * @return String
     */
    public static String mesasPorMeseros() {
        // Creamos e inicializamos el String a devolver
        String mesas_por_meseros = "Mesero\t|\tMesas";
        // Para cada mesero
        for (Mesero ca : meseros) { 
            // Ampliamos el String a devolver con los datos del mesero
            mesas_por_meseros += "\n" + ca.getNombre() + "\t|\t" + ca.getMesasAtendidas();
        }
        return mesas_por_meseros;
    }

    /**
     * Getter. Función que obtiene los ingresos de una mesa que se indica como argumento.
     * @param idMesa
     * @return int
     */
    public static int getIngresosMesa(int idMesa) {
        // Creamos e inicializamos la variable a retornar
        int ingreso = 0;
        // Para cada elemento del ArrayList de mesas
        for (Mesa me : mesas) {
            // Si el id introducido coincide con la mesa evaluada
            if (me.getId() == idMesa) {
                // Consulta el consumo acumulado de la mesa
                ingreso = me.getIngresoAcumulado();
                // Rompemos el bucle for para evitar iteraciones innecesarias
                break;
            }
        }
        // Devolvemos el valor
        return ingreso;
    }

    /**
     * Función que retorna un entero con la cantidad de ingreso actual de una mesa que se indica como argumento.
     * @param idMesa
     * @return int
     */
    public static int consultarConsumoMesa(int idMesa) {
        // Creamos e inicializamos la variable a retornar
        int consumo = 0;
        // Para cada elemento del ArrayList de mesas
        for (Mesa me : mesas) {
            // Si el id introducido coincide con la mesa evaluada
            if (me.getId() == idMesa) {
                // Consulta el consumo actual de la mesa
                consumo = me.getIngresoActual();
                // Rompemos el bucle for para evitar iteraciones innecesarias
                break;
            }
        }
        // Devolvemos el valor
        return consumo;
    }

    /**
     * Función que cierra la cuenta de una mesa pasada como argumento y le adjudica el cierre al camarero también especificado en el segundo argumento.
     * @param idMesa
     * @param Nombre
     */
    public static void cerrarCuentaMesa(int idMesa, String Nombre) {
        // Buscar dentro del ArrayList de mesas
        for (Mesa me : mesas) {
            // Cuando la cuenta mesa que queremos cerrar coincida con el id
            if (me.getId() == idMesa) {
                // Buscar dentro del ArrayList de meseros
                for (Mesero ca : meseros) {
                    // Cuando el nombre del camarero que cierra la mesa coincida
                    // con el nombre del camarero que la cierra
                    if (ca.getNombre().equalsIgnoreCase(Nombre)) {
                        // Cerramos la cuenta de la mesa
                        me.cerrarCuenta();
                        // Incrementamos el numero de mesas atendidas
                        ca.incrementarMesasAtendidas();
                        // Salimos del for (para que no itere más veces innecesariamente)
                        break;
                    }
                }
                // Salimos del for (para que no itere más veces innecesariamente)
                break;
            }
        }
    }

    /**
     * Función que retorna un String con los productos consumidos en la actualidad por la mesa especificada como argumento.
     * @param idMesa
     * @return
     */
    public static String consultarProductosMesa(int idMesa) {
        // Cabecera
        String ProdMesa = "Producto\t|\tCantidad";
        int articulos=0;
        // Para cada mesa
        for (Mesa me : mesas) {
            // Buscamos la que coincida con la pasada en el argumento
            if (me.getId() == idMesa) {
                // Obtenemos la lista de productos consumidos en una mesa
                ArrayList<Producto> aux = me.getProductosActual();
                // Usamos una matriz para añadir los diferentes productos
                // Tamaño de la matriz: 
                // 1: "productos.size()+1" para que no haya excepción de fuera del indice
                // 2: 1 por que sólo habrá un número.
                int[][] lista_de_productos = new int[productos.size() + 1][1];
                // Para cada producto de la lista auxiliar
                for (Producto pro : aux) {
                    // Añadimos en la correspondiente posición de la matriz
                    lista_de_productos[pro.getId()][0] = lista_de_productos[pro.getId()][0] + 1;
                }
                
                // Procedemos al llenado del String ahora ya con la lista de productos completada (productos repetidos sumados)
                for (int i = 0; i < lista_de_productos.length; i++) {
                    // Si hay un producto que tiene 0 repeticiones, debe ignorarlo
                    if (lista_de_productos[i][0] != 0) {
                        String nombreProducto = "";
                        // Busca el nombre del producto a añadir
                        for (Producto p : productos) {
                            if (p.getId() == i) {
                                nombreProducto = p.getNombre();
                                break;
                            }
                        }
                        // Una vez lo tiene lo añade al String
                        ProdMesa += "\n" + nombreProducto + "\t|\t" + lista_de_productos[i][0];
                        articulos+=lista_de_productos[i][0];
                    }
                }
                ProdMesa+="\nTotal = "+articulos;
                break;
            }
        }
        return ProdMesa;
    }

    /**
     * Función que agrega un producto especificado a una mesa especificada.
     * @param idMesa
     * @param idProducto
     */
    public static void agregarPedidoMesa(int idMesa, int idProducto) {
        // Busca para cada mesa
        for (Mesa m : mesas) {
            // La mesa correspondiente al argumento
            if (m.getId() == idMesa) {
                // Busca para cada producto
                for (Producto p : productos) {
                    // El producto correspondiente al argumento
                    if (p.getId() == idProducto) {
                        // Finalmente agrega el producto
                        m.agregarPedido(p);
                        break;
                    }
                }
                break;
            }
        }
    }

    /* Funciones auxiliares del método main */
    
    /**
     * Función que imprime el menú de opciones general, y retorna la opción VALIDA escogida.
     * @return int
     */
    public static int imprime_menu() {
        // Variables de control de la función
        int seleccion = 0;
        boolean ok = false;
        // Bucle principal de la función
        while (!ok) {
            // Muestra el menú
            System.out.print("\nElegir una opcion:" +
                    "\n1 - Agregar producto mesa\n" +
                    "2 - Consultar consumo mesa\n" +
                    "3 - Cerrar cuenta mesa\n" +
                    "4 - Consultar ingreso mesa\n" +
                    "5 - Consultar ingresos totales\n" +
                    "6 - Consultar mesas por mesero\n" +
                    "0 - Salir\n" +
                    "\n--> ");
            // Obtiene el valor
            BufferedReader br = null;
            try {
                br = new BufferedReader(new InputStreamReader(System.in));
                seleccion = Integer.parseInt(br.readLine());
                // Mira que el número esté en un rango correcto
                if (seleccion >= 0 && seleccion <= 6) {
                    ok = true;
                }
            // Control de errores
            } catch (NumberFormatException nfe) {
                System.err.println("ERROR: Introduzca un valor numerico comprendido entre 0 y 6");
            } catch (IOException ioe) {
                System.err.println("ERROR: ");
                ioe.printStackTrace();
                System.exit(1);
            } // finally { /* Cerrar el BufferedReader lanza excepciones */}
        }
        // Retorno del resultado válido
        return seleccion;
    }

    /**
     * Función que pide el número de mesa y retorna un número de mesa válido.
     * @return int
     */
    public static int pideNumMesa() {
        // Variables de control de la función
        int NumMesa = 0;
        boolean ok = false;
        // Bucle principal de la función
        while (!ok) {
            // Pide el número de mesa
            System.out.print("\nIngrese el numero de mesa: ");
            BufferedReader br = null;
            try {
                br = new BufferedReader(new InputStreamReader(System.in));
                NumMesa = Integer.parseInt(br.readLine());
                // Si el número de mesa es menor que el tamaño de la lista de mesas y este además es mayor que cero. Será un valor válido.
                // Damos por sentado que las mesas tienen un identificador correlativo por tal y como lo hemos codificado.
                if (NumMesa < mesas.size() && NumMesa >= 0) {
                    ok = true;
                }
                // Control de errores
            } catch (NumberFormatException nfe) {
                System.err.println("ERROR: Introduzca un valor numerico valido.");
                nfe.printStackTrace();
            } catch (IOException ioe) {
                System.err.println("ERROR: ");
                ioe.printStackTrace();
                System.exit(1);
            } 

            if (!ok) {
                System.out.println("Introduzca un numero de mesa valido.");
            }
        }
        return NumMesa;
    }

    /**
     * Función que pide el id del producto y retorna un id de producto válido.
     * @return
     */
    public static int pideProductoId() {
        // Variables de control de la función
        int ProductoId = 0;
        boolean ok = false;
        // Bucle principal de la función
        while (!ok) {
            // Pide el número de producto
            System.out.print("\nIngrese el id del producto: ");
            BufferedReader br = null;
            try {
                br = new BufferedReader(new InputStreamReader(System.in));
                ProductoId = Integer.parseInt(br.readLine());
                if (!productos.isEmpty()) {
                    // Para los productos
                    for (Producto p : productos) {
                        // Busca el correspondiente al leído
                        if (p.getId() == ProductoId) { // El id existe
                            ok = true;
                            break;
                        }
                    }
                }
                // Control de errores
            } catch (NumberFormatException nfe) {
                System.err.println("ERROR: Introduzca un valor numerico valido.");
            } catch (IOException ioe) {
                System.err.println("ERROR: ");
                ioe.printStackTrace();
                System.exit(1);
            }

            if (!ok) {
                System.out.println("Ha introducido un id no valido.");
            }
        }
        return ProductoId;
    }

    /**
     * Función que pide el nombre del mesero y retorna un String con un nombre válido.
     * @return
     */
    public static String pideNombreMesero() {
        // Variables de control de la función
        String nombreMesero = "";
        boolean ok = false;
        // Bucle principal de la función
        while (!ok) {
            // Pide el nombre del camarero
            System.out.print("\nIngrese el nombre del mesero: ");
            BufferedReader br = null;
            try {
                br = new BufferedReader(new InputStreamReader(System.in));
                nombreMesero = br.readLine();
                // Comprobamos que el nombre introducido existe en la lista de meseros
                for (Mesero ca : meseros) {
                    if (ca.getNombre().equalsIgnoreCase(nombreMesero)) {
                        ok = true;
                        break;
                    }
                }
                // Control de errores 
            } catch (IOException ioe) {
                System.err.println("ERROR: ");
                ioe.printStackTrace();
                System.exit(1);
            }

            if (!ok) {
                System.out.println("Por favor introduzca un nombre de mesero valido.");
            }
        }
        return nombreMesero;
    }

    /**
     * Método main.
     * @param args
     */
    public static void main(String[] args) {
        Restaurant r = new Restaurant("meseros.txt", 9, "productos.txt");
        // Variables utilizadas
        int numMesa;
        int productoId;
        String nombreMesero;
        // Bucle principal
        while (true) {
            switch (imprime_menu()) {
                case 0:
                    // Salir
                    System.exit(0);
                    break;
                case 1:
                    // Agregar producto mesa
                    // ---------------------
                    // Pedir numero de mesa
                    numMesa = pideNumMesa();
                    // Imprimir lista de productos
                    System.out.println(listarProductos());
                    // Pedir id del producto
                    productoId = pideProductoId();
                    // Agregar el producto
                    agregarPedidoMesa(numMesa, productoId);
                    break;
                case 2:
                    // Consultar consumo mesa
                    // ----------------------
                    // Pedir numero de mesa
                    numMesa = pideNumMesa();
                    // Imprimir consumo de la mesa
                    System.out.println(consultarProductosMesa(numMesa));
                    break;
                case 3:
                    // Cerrar cuenta mesa
                    // -------------------
                    // Pedir numero de mesa
                    numMesa = pideNumMesa();
                    // Pedir nombre del camarero
                    nombreMesero = pideNombreMesero();
                    // Cerrar la cuenta
                    cerrarCuentaMesa(numMesa, nombreMesero);
                    break;
                case 4:
                    // Consultar ingreso mesa
                    // ----------------------
                    // Pedir numero de mesa
                    numMesa = pideNumMesa();
                    // Imprimir el ingreso de la mesa
                    System.out.println("Los ingresos de la mesa " + numMesa +
                            " ascienden a un total de " + getIngresosMesa(numMesa) + " euros.\n");
                    break;
                case 5:
                    // Consultar ingresos totales
                    System.out.println("Los ingresos totales ascienden a un total de " +
                            ingresosTotales() + " euros.\n");
                    break;
                case 6:
                    // Consultar mesas por mesero
                    System.out.println(mesasPorMeseros());
                    break;
                default:
                    // No debería de pasar nunca
                    System.exit(-1);
                    break;
            }
        }
    }
}

